package cn.com.captureNetPacket;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

import org.jnetpcap.protocol.network.Ip4;
import org.jnetpcap.protocol.tcpip.Tcp;
import org.springframework.stereotype.Component;

import cn.com.captureNetPacket.model.NetworkInfo;
import cn.com.captureNetPacket.util.Log2File;
import cn.com.captureNetPacket.util.Tools;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AnalysisTCP   extends Analysis {

	/*
(1)端口号[16bit]

我们知道，网络实现的是不同主机的进程间通信。在一个操作系统中，有很多进程，当数据到来时要提交给哪个进程进行处理呢?这就需要用到端口号。在TCP头中，有源端口号(Source Port)和目标端口号(Destination Port)。源端口号标识了发送主机的进程,目标端口号标识接受方主机的进程。

(2)序号[32bit]

序号分为发送序号(Sequence Number)和确认序号(Acknowledgment Number)。

发送序号：用来标识从 TCP源端向 TCP目的端发送的数据字节流，它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动，则 TCP用顺序号对每个字节进行计数。序号是 32bit的无符号数，序号到达 2  32－ 1后又从 0开始。当建立一个新的连接时， SYN标志变 1，顺序号字段包含由这个主机选择的该连接的初始顺序号 ISN（ Initial Sequence Number）。

确认序号：包含发送确认的一端所期望收到的下一个顺序号。因此，确认序号应当是上次已成功收到数据字节顺序号加 1。只有 ACK标志为 1时确认序号字段才有效。 TCP为应用层提供全双工服务，这意味数据能在两个方向上独立地进行传输。因此，连接的每一端必须保持每个方向上的传输数据顺序号。

(3)偏移[4bit]

这里的偏移实际指的是TCP首部的长度，它用来表明TCP首部中32 bit字的数目，通过它可以知道一个TCP包它的用户数据是从哪里开始的。这个字段占4bit,如4bit的值是0101,则说明TCP首部长度是5 * 4 = 20字节。 所以TCP的首部长度最大为15 * 4 = 60字节。然而没有可选字段，正常长度为20字节。

(4)Reserved [6bit]

目前没有使用，它的值都为0

(5)标志[6bit]

在TCP首部中有6个标志比特。他们中的多个可同时被置为1 。
SYN          同步序号用来发起一个连接
FIN            发送端完成发送任务(即断开连接)

URG         紧急指针(urgent pointer)有效
ACK          确认序号有效
PSH          指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满

RST           一般表示断开一个连接
	 * ******************************************************
	 * Flag标志位：
     Nonce Sum：有效排除潜在的ECN滥用，RFC 3540
     Congestion Window Reduced（CWR）：拥塞窗口减少标志
     ECN-Echo：ECE / ECN标志
     URG: 紧急指针有效（urgentpointer） 当URG=1时，表明此报文应尽快传送，而不要按原来的排队顺序来传送。与“紧急指针”字段配合使用，紧急指针指出在本报文段中的紧      急数据的最后一个字节的序号，使接收方可以知道紧急数据共有多长；
     ACK：确认序号有效,只有当ACK=1时，确认序号字段才有意义；
     PSH：当PSH=1时，表明请求远地TCP将本报文段立即传送给其应用层，而不要等到整个缓存都填满了之后再向上交付。
     RST：重建连接,当RST=1时，表明出现严重差错，必须释放连接，然后再重建传输连接。复位比特还用来拒绝一个非法的报文段或拒绝打开一个连接；
     SYN：同步序号用来发起一个连接,在建立连接时使用，当SYN=1而ACK=0时，表明这是一个连接请求报文段。对方若同意建立连接，在发回的报文段中使SYN=1和ACK=1。因此，SYN=1表示这是一个连接请求或连接接受报文，而ACK的值用来区分是哪一种报文；
     FIN： 发端完成发送任务（主动关闭）,用来释放一个连接，当FIN=1时，表明欲发送的字节串已经发完，并要求释放传输连接；
	*/
	private StringBuffer text=new StringBuffer();
	private static  ConcurrentHashMap<String ,AnalysisTCP> analysisTCPs=new ConcurrentHashMap<String,AnalysisTCP>();
	byte[] data=new byte[0];
	//AcknowledgementNumber 确认序列号
	//SequenceNumber  字节码的序列号 ， 序列号（相对序列号，此序列号用来确定传送数据的正确位置，且序列号，用来侦测丢失的包）；
	//ipIdentification ip层的顺序号，应用程序给的 
	
	
	//如果不是syn，ret，fin，那么下一个SequenceNumber=当前的SequenceNumber+数据长度的长度
	private NetworkInfo netInfo;int ipIdentification=-1,packetSize=-1;
	private long sequenceNumber=-1,nextSequenceNumber=-1,acknowledgementNumber=-1;
	// private Ip4.Timestamp timestamp = new Ip4.Timestamp(); // Optional header
	private TreeMap<Integer ,Tcp> tcpPackets=new TreeMap<Integer,Tcp>();
	
	@Override
	public void analysisPackageData() {
		try {
			 synchronized(tcpPackets) { 
		   // tcpPackets.wait();
		     for (Tcp tcp: tcpPackets.values()) {
					analysisTcpPacket(tcp);
				}
		//   tcpPackets.wait();
		     }
		} catch ( Exception e) {
			 log.info("InterruptedException:",e);
		}
	
	}
	
	
	public void addPacket(Ip4 ip,Tcp tcp){
		
		
		ipIdentification=ip.id();
	//	ip.hasSubHeader(timestamp);//获取抓取到的时间
		 
		sequenceNumber=tcp.seq();
		acknowledgementNumber=tcp.ack();
		packetSize=tcp.getPayloadLength();
		tcpPackets.put(ipIdentification, tcp);
		log.warn("  "+netInfo.getNetworkInfoKey()+" -------------》"+"  FrameNumber:"+netInfo.getFrameNumber());
		Log2File.writeFile(this.netInfo.getNetworkInfoKey(),"SYN  =ACK  /PSH  =RST  \\FIN  =URG  ||CWR  =ECE  : \r\n"+tcp.flags_SYN()+"="+tcp.flags_ACK()+"/"+tcp.flags_PSH()+"="+tcp.flags_RST()+"\\"+tcp.flags_FIN()+"="+tcp.flags_URG()+"||"+tcp.flags_CWR()+"="+tcp.flags_ECE()+" ipid:"+ipIdentification+"  FrameNumber:"+netInfo.getFrameNumber()+" seq:"+sequenceNumber+" packetSize:"+packetSize+" acknowledgementNumber:"+acknowledgementNumber);
		log.warn("SYN=ACK/PSH=RST\\FIN=URG||CWR=ECE: "+tcp.flags_SYN()+"="+tcp.flags_ACK()+"/"+tcp.flags_PSH()+"="+tcp.flags_RST()+"\\"+tcp.flags_FIN()+"="+tcp.flags_URG()+"||"+tcp.flags_CWR()+"="+tcp.flags_ECE()+"  "+netInfo.getNetworkInfoKey() +" ipid:"+ipIdentification+"  FrameNumber:"+netInfo.getFrameNumber()+" seq:"+sequenceNumber+" packetSize:"+packetSize+" acknowledgementNumber:"+acknowledgementNumber);
		
		 
		try{ 
			synchronized(tcpPackets){
				//获取
			 
				getTPKT(tcp.getPayload());
					 if(tcp.flags_RST()||tcp.flags_FIN()||tcp.flags_PSH())
					 {
						 
						// startAnalysis();
//					  new Thread(this).start();
						//Thread.sleep(10000);
						// tcpPackets.notifyAll();
						// log.error("addPacketaaaaaaccccccnotifyAll："+ipIdentification);
					  
					   
					 }
			}
		}catch (Exception e) {
			//log.error("addPacket:"+e.getMessage());
		 	log.error("addPacket:"+e.getMessage(),e);
		}
	}
	
	
	 public byte[] getTPKT(byte[] bs) {
		 if(bs.length==0)return null;
		// TPKT协议这4个字节分别是0x03, 0x00, HH, LL. 0x03 表示TPKT的版本,0x00是保留字段，HH 和LL 表示整个H.225信息的长度, 包括TPKT头部。
		 if(bs[0]==0x03&& bs[1]==0x00){
			 byte[] lengthbs= new byte[2];
			 System.arraycopy(bs, 2,lengthbs, 0,2);
			 //第3,4字节为长度
		 log.info(bs.length+"------------TCP数据长度包含整个长度，与TPKT标志长度一样----is TPKT:长度："+Tools.readInt(lengthbs));
		 }
		 //如果第5字节  为0x08Protocol Discriminator(协议标识):0x08(ISDN)、1字节长度
		 if(bs[4]==0x08){
			// Call Reference(呼叫参考):每路呼叫标识号码，1，2或者3字节长度
			// Message Type(消息类型):标识消息功能，比如setup、connect、release等
		//	 bs[5] 为长度
			if(bs[5]==0) {
				
				
			}
			 
			 
		 }
		 
		 //TPKT报文消息
		 byte[] newBuffer = Arrays.copyOf(bs, bs.length);
		 
		 return newBuffer;
		
	}


	public void analysisTcpPacket(Tcp tcp){
		byte[]  currentPayload=new byte[0],newdata;
		String description="";
		if(tcp.getPayload().length!=0)
			 currentPayload=tcp.getPayload().clone();
			if(tcp.flags_RST()){
				//发生错误，重发
				description="重发";
				data=new byte[0];
			}
			if(tcp.flags_FIN()){
				//完成，关闭连接
				
				description="关闭";
				newdata=new byte[data.length+currentPayload.length];
				System.arraycopy(data, 0, newdata, 0, data.length);
				System.arraycopy(currentPayload,0 , newdata,data.length, currentPayload.length);
				analysisMorePayload(newdata);
				data=new byte[0];
				tcpPackets.notify();
			}
			 if(tcp.flags_SYN())
			 {
				 if(tcp.flags_ACK())description="接受";
				 else description="请求";
				 data=new byte[0];
			 }
			
			 
			if(tcp.flags_ACK())
			{
				description="确认";
				newdata=new byte[data.length+currentPayload.length];
				System.arraycopy(data, 0, newdata, 0, data.length);
				System.arraycopy(currentPayload,0 , newdata,data.length, currentPayload.length);
				nextSequenceNumber=sequenceNumber+currentPayload.length;
			}
			 if(tcp.flags_PSH()){
				 //立即提交到上一层应用去处理
				 description="提交";
				 newdata=new byte[data.length+currentPayload.length];
					System.arraycopy(data, 0, newdata, 0, data.length);
					System.arraycopy(currentPayload,0 , newdata,data.length, currentPayload.length);
					nextSequenceNumber=sequenceNumber+currentPayload.length;
					analysisMorePayload(newdata);
					tcpPackets.notify();
					 
			 }
			//如果数据部分长度为0，那么这个一定是一个标识消息。下一个消息一定是一个新的报文
			 if(currentPayload.length==0)data=new byte[0];
			 
			 
		 	 
	}
	public void analysisMorePayload(byte[] data){ 
		try {
		 	// analysis(Ip4 ip,Tcp tcp)
		log.info("ipIdentification:"+ipIdentification+" length:"+data.length+"\r\n"+new String(data,"UTF-8"));
	} catch (UnsupportedEncodingException e) {
		e.printStackTrace();
	}
	}
	
	
	
	
	
	
	
	
	
	
	public StringBuffer getText() {
		return text;
	}
	public void setText(StringBuffer text) {
		this.text = text;
	}
	public static ConcurrentHashMap<String, AnalysisTCP> getAnalysisTCPs() {
		return analysisTCPs;
	}
	public static void setAnalysisTCPs(ConcurrentHashMap<String, AnalysisTCP> analysisTCPs) {
		AnalysisTCP.analysisTCPs = analysisTCPs;
	}
	public NetworkInfo getNetInfo() {
		return netInfo;
	}
	public void setNetInfo(NetworkInfo netInfo) {
		this.netInfo = netInfo;
	}


	@Override
	public void run() {
		analysisPackageData();
		
	}


	@Override
	public void initAnalysis() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void analysisSortPackageData() {

	}


	@Override
	public void endAnalysis() {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void handelAllPackageData() {
		// TODO Auto-generated method stub
		
	}
	 
	 
	
	
	
	 
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
}
